package com.fengpb.friday.conductor.common.metadata.run;

import com.fengpb.friday.conductor.common.metadata.Auditable;
import com.fengpb.friday.conductor.common.metadata.task.Task;
import com.fengpb.friday.conductor.common.metadata.workflow.WorkflowDef;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class Workflow extends Auditable {

    public enum WorkflowStatus {

        RUNNING(false, false),
        COMPLETED(true, true),
        FAILED(true, false),
        TIMED_OUT(true, false),
        TERMINATED(true, false)
        ;
        private boolean terminal;

        private boolean successful;

        WorkflowStatus(boolean terminal, boolean successful){
            this.terminal = terminal;
            this.successful = successful;
        }
        public boolean isTerminal(){
            return terminal;
        }

        public boolean isSuccessful() {
            return successful;
        }
    }

    private WorkflowStatus status = WorkflowStatus.RUNNING;

    private long startTime;

    private long endTime;

    private String workflowId;

    private String parentWorkflowId;

    private String parentWorkflowTaskId;

    private List<Task> tasks = new LinkedList<>();

    private Map<String, Object> input = new HashMap<>();

    private Map<String, Object> output = new HashMap<>();;

    private WorkflowDef workflowDefinition;

    private String reasonForInCompletion;

    public WorkflowStatus getStatus() {
        return status;
    }

    public void setStatus(WorkflowStatus status) {
        this.status = status;
    }

    public long getStartTime() {
        return startTime;
    }

    public void setStartTime(long startTime) {
        this.startTime = startTime;
    }

    public long getEndTime() {
        return endTime;
    }

    public void setEndTime(long endTime) {
        this.endTime = endTime;
    }

    public String getWorkflowId() {
        return workflowId;
    }

    public void setWorkflowId(String workflowId) {
        this.workflowId = workflowId;
    }

    public String getParentWorkflowId() {
        return parentWorkflowId;
    }

    public void setParentWorkflowId(String parentWorkflowId) {
        this.parentWorkflowId = parentWorkflowId;
    }

    public String getParentWorkflowTaskId() {
        return parentWorkflowTaskId;
    }

    public void setParentWorkflowTaskId(String parentWorkflowTaskId) {
        this.parentWorkflowTaskId = parentWorkflowTaskId;
    }

    public List<Task> getTasks() {
        return tasks;
    }

    public void setTasks(List<Task> tasks) {
        this.tasks = tasks;
    }

    public Map<String, Object> getInput() {
        return input;
    }

    public void setInput(Map<String, Object> input) {
        this.input = input;
    }

    public Map<String, Object> getOutput() {
        return output;
    }

    public void setOutput(Map<String, Object> output) {
        this.output = output;
    }

    public WorkflowDef getWorkflowDefinition() {
        return workflowDefinition;
    }

    public void setWorkflowDefinition(WorkflowDef workflowDefinition) {
        this.workflowDefinition = workflowDefinition;
    }

    public String getReasonForInCompletion() {
        return reasonForInCompletion;
    }

    public void setReasonForInCompletion(String reasonForInCompletion) {
        this.reasonForInCompletion = reasonForInCompletion;
    }

    public String getWorkflowName() {
        return getWorkflowDefinition() != null ?
                getWorkflowDefinition().getName() :
                "";
    }

    public int getWorkflowVersion() {
        return getWorkflowDefinition() != null ?
                getWorkflowDefinition().getVersion() :
                0;
    }

    public Task getTaskByRefName(String refName) {
        if (refName == null) {
            throw new RuntimeException("refName passed is null.  Check the workflow execution.  For dynamic tasks, make sure referenceTaskName is set to a not null value");
        }
        LinkedList<Task> found = new LinkedList<>();
        for (Task t : tasks) {
            if (t.getReferenceTaskName() == null) {
                throw new RuntimeException("Task " + t.getTaskDefName() + " does not have reference name specified.");
            }
            if (t.getReferenceTaskName().equals(refName)) {
                found.add(t);
            }
        }
        if (found.isEmpty()) {
            return null;
        }
        return found.getLast();
    }
}
